#include "pix5core.h"



//
// Table maintenance operations
//
#define TMO_ADD				0x01
#define TMO_DEL				0x02
#define TMO_TYPE			0x03
#define TMO_SIZE			0x04




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



Table::Table()
{
	_tableDef = NULL;
}
	
Table::Table(Table* tableDef)
{
	_tableDef = tableDef;

	// Copy the fields links
	for(int c=0;c<tableDef->_fields.getSize();c++)
		_fields.add((TableFieldBase*)(((char*)this)+((int)(((char*)(tableDef->_fields[c])) - ((char*)tableDef)))));
}

Table::~Table()
{
	_fields.clear();
}

void Table::backup()
{
	// Create backups for all the data files
	String timeStamp = DateTime::now().getTimeStamp();

	IO::copyFile(_basePath+".def",_baseBackupPath+".def."+timeStamp);
	//IO::copyFile(_basePath+".dat",_basePath+".dat"+"_backup_"+timeStamp);
}
	
void Table::loadDef()
{
	// Open the definition file
	File* f;

	try
	{
		f = IO::openFile(_basePath+".def");
	}
	catch(Error)
	{
		// The file could not be open
		// It must not exist, create it
		try
		{
			f = IO::openFile(_basePath+".def",true,true);
		}
		catch(Error e)
		{
			// Oops
			throw Error("Could not open or create table definition file '%s': %s",(const char*)(_basePath+".def"),(const char*)e);
		}
	}

	// Load the definition data
	Buffer buf;

	try
	{
		// Read the data
		int size = (int)f->getSize();

		if(size)
		{
			buf.resize(size,false);
			f->read((char*)buf,size);
		}

		delete f;
	}
	catch(Error)
	{
		delete f;
		throw;
	}
		
	// Validate the definition
	validateDef(buf);
}

void Table::validateDef(Buffer& buf)
{
	// Validate a table definition file

	// First check for corruption
	if((buf.getSize()%sizeof(TableFieldDef))!=0)
		throw Error("Table definition file is corrupt: %s",(const char*)(_basePath+".def"));

	int numDef = buf.getSize()/sizeof(TableFieldDef);
	TableFieldDef* defs = (TableFieldDef*)(char*)buf;

	// Check if any maintenance is required
	bool mustFix = false;

	// Check if the data file has fields to be removed
	for(int c=0;c<numDef;c++)
	{
		bool found = false;

		for(int d=0;d<_fields.getSize();d++)
			if(_fields[d]->_name==defs[c]._name)
			{
				found = true;
				break;
			}

		if(!found)
		{
			// The field is in the file, but not in the C++ definition
			// A fix is required
			mustFix = true;
			break;
		}
	}

	// Check all the C++ defined fields and compare to the file
	for(int c=0;c<_fields.getSize();c++)
	{
		TableFieldBase* field = _fields[c];

		// Look for the field in the file definitions
		bool found = false;

		for(int d=0;d<numDef;d++)
		{
			if(field->_name==defs[d]._name)
			{
				// Found it, mark the offset of the field in the old data file
				field->_oldType = defs[d]._type;
				field->_oldSize = defs[d]._size;
				field->_oldOffset = defs[d]._offset;

				field->_offset = defs[d]._offset;
				
				// Check if its type or size changed
				if(field->_type!=defs[d]._type || field->_size!=defs[d]._size)
					// Type or size changed, a fix is required
					mustFix = true;

				found = true;
				break;
			}
		}

		if(!found)
		{
			// The field is not in the file
			field->_offset = 0;

			// A fix is required
			mustFix = true;
		}
	}

	// Perform any required maintenance
	if(mustFix)
		fixAll();
}
	
void Table::fixAll()
{
	printf("Fixing table '%s'...\n",(const char*)_basePath);

	// Backup the old table data
	try
	{
		backup();
	}
	catch(Error e)
	{
		throw Error("Error while backuping table files '%s.*': %s",(const char*)_basePath,(const char*)e);
	}

	printf("Rebuilding definition...\n");

	// Rebuild raw definition data
	Buffer buf(_fields.getSize()*sizeof(TableFieldDef));
	TableFieldDef* defs = (TableFieldDef*)(char*)buf;
	int offset = 8;

	for(int c=0;c<_fields.getSize();c++)
	{
		TableFieldBase* field = _fields[c];

		defs[c]._type = field->_type;
		defs[c]._size = field->_size;
		defs[c]._offset = offset;
		strcpy(defs[c]._name,(const char*)field->_name);

		offset += field->_size;
	}

	// Write the definition file
	File* f = NULL;

	try
	{
		f = IO::openFile(_basePath+".def",true,true);
		f->write((char*)buf,buf.getSize());
	}
	catch(Error e)
	{
		if(f)
			delete f;

		throw Error("Error while rebuilding table definition file '%s': %s",(const char*)(_basePath+".def"),(const char*)e);
	}

	delete f;

	// Fix the data
	fixData(defs);

}
	
void Table::fixData(TableFieldDef* defs)
{
	printf("Fixing data...\n");

	printf("New layout: %i fields\n",_fields.getSize());
	printf("Name                         (old) Type   Off   Size      (new) Type   Off   Size\n");
	printf("---------------------------------------------------------------------------------\n");
	for(int c=0;c<_fields.getSize();c++)
	{
		TableFieldBase* field = _fields[c];

		if(field->_offset==0)
			printf("%-32s   (NEW FIELD)                  %i      %-5i %i\n",(const char*)field->_name,defs[c]._type,defs[c]._offset,defs[c]._size);
		else
			printf("%-32s   %i      %-5i %-5i           %i      %-5i %i\n",(const char*)field->_name,field->_oldType,field->_oldOffset,field->_oldSize,defs[c]._type,defs[c]._offset,defs[c]._size);
	}
}

void Table::registerField(TableFieldBase* field,const String& name,int type,int size)
{
	// Initialize the field
	field->_table = this;
	field->_name = name;
	field->_type = type;
	field->_size = size;
	field->_offset = 0;

	// Add it to the know fields array
	_fields.add(field);
}

void Table::commit()
{
	ASSERT(_tableDef,"This table has no definition - use insert() or update() to obtain a table instance")

	printf("Commit:\n");

	for(int c=0;c<_tableDef->_fields.getSize();c++)
	{
		TableFieldBase* field = _tableDef->_fields[c];

		printf("%32s : ",(const char*)field->_name);

		switch(field->_type)
		{
			case TFT_INT:
				printf("(int) %i\n",(int)*((TableField<int>*)_fields[c]));
				break;

			case TFT_LNG:
				printf("(lng) %lli\n",(long long)*((TableField<long long>*)_fields[c]));
				break;

			case TFT_DBL:
				printf("(dbl) %f\n",(double)*((TableField<double>*)_fields[c]));
				break;

			case TFT_STR:
				printf("(str) %s\n",(const char*)(String)*((TableField<String>*)_fields[c]));
				break;

			case TFT_BIN:
				break;

			default:
				ASSERT(false,"Internal error - unhandled field type")
		}
	}
}


